"""dprtree

Build a tree of used units in a .dpr project
"""

import os.path
from .unitsearcher import DelphiUnitSearcher
from .dprparser import parse_dpr_uses, parse_pas_uses, UnitInUses
from .dcudeps import get_dcu_deps
from .utils import warning, relpath


def dprtree(dprfn):

    searcher = DelphiUnitSearcher(project=dprfn)
    new = []

    def process_dcu(fn):
        try: 
            srcs, deps = get_dcu_deps(fn)
        except Exception, e: 
            warning(u"Could not process '%s' (%s)" % (fn, e))
            return
        for uname in deps:
            if u'.' in uname:
                continue
            if not searcher.is_known(uname):
                unit = searcher.locate_unit(uname)
                yield unit
                new.append(unit)

    def process_pas(fn):
        try: 
            uses_interface, uses_implementation = parse_pas_uses(fn)
        except Exception, e: 
            warning(u"Could not process '%s' (%s)" % (fn, e))
            return
        for unit in uses_interface + uses_implementation:
            if not searcher.is_known(unit.name):
                unit = searcher.locate_unit(unit.name)
                yield unit
                new.append(unit)

    def process_dpr(fn):
        try:
            _, uses_list, _ = parse_dpr_uses(fn)
        except Exception, e: 
            warning(u"Could not process '%s' (%s)" % (fn, e))
            return
        for unit in uses_list:
            if unit.path:
                unit = searcher.locate_unit(unit.path)
            else:
                unit = searcher.locate_unit(unit.name)
            yield unit
            new.append(unit)

    def tree_loop(fn):
        pending = []
        new.append(UnitInUses(fn, fn))
        while new:
            pending[:] = new
            del new[:]
            for unit in pending:
                ext = unit.path[-4:].lower()
                if ext==u'.dcu':
                    yield process_dcu, unit.path
                elif ext==u'.pas':
                    dcu = os.path.join(searcher.project_dcu_dir, unit.name + u'.dcu')
                    if os.path.isfile(dcu):
                        yield process_dcu, dcu
                    else:
                        yield process_pas, unit.path
                elif ext==u'.dpr':
                    yield process_dpr, fn

    for state in tree_loop(dprfn):
        function = state[0]
        args = state[1:]
        for unit in function(*args):
            yield unit

def rescan_dpr(dpr):
    dprdir = os.path.abspath(os.path.dirname(dpr))
    before, orig_uses, after = parse_dpr_uses(dpr)
    expanded_uses = dict((u.name.lower(), u) for u in dprtree(dpr))
    uses = []
    for ounit in orig_uses:
        key = ounit.name.lower()
        eunit = expanded_uses.pop(key, None)
        if eunit and eunit.path:
            ounit.path = relpath(eunit.path, dprdir)
        uses.append(ounit)
    for key,eunit in sorted(expanded_uses.iteritems()):
        if not eunit.head:
            eunit.head += u'\n  '
        if eunit.path:    
            eunit.path = relpath(eunit.path, dprdir)
        uses.append(eunit)
    return before, uses, after

def regenerate_dpr(dpr):
    before, uses, after = rescan_dpr(dpr)
    src = before + u','.join(unicode(u) for u in uses) + after        
    return src

def extractdirs_dpr(dpr):
    _, uses, _ = rescan_dpr(dpr)
    del _
    dirs = sorted(set(os.path.dirname(u.path) for u in uses))
    return dirs

